package pl.wroc.uni.ii.evolution.engine.operators.spacespecific.binaryvector;

import java.util.ArrayList;
import java.util.Iterator;

import pl.wroc.uni.ii.evolution.engine.EvPopulation;
import pl.wroc.uni.ii.evolution.engine.individuals.EvBinaryVectorIndividual;
import pl.wroc.uni.ii.evolution.engine.operators.general.selections.EvTournamentSelection;
import pl.wroc.uni.ii.evolution.engine.operators.spacespecific.binaryvector.ecga.EvPopulationGenerating;
import pl.wroc.uni.ii.evolution.engine.operators.spacespecific.binaryvector.ecga.EvStructureDiscover;
import pl.wroc.uni.ii.evolution.engine.prototype.EvOperator;
import pl.wroc.uni.ii.evolution.solutionspaces.EvBinaryVectorSpace;

/**
 * Class implementing ECGA algorithm with {@link EvTournamentSelection} as 
 * operator.
 * <br>
 * This operator uses three operators: EvTournamenSelection, 
 * EvStructureDiscover, EvPopulationGenerating.
 * <br>
 * At first selection is done by EvTournamentSelection.
 * Then EvStructureDiscover using population discover the best partition of chromosome into blocks.
 * Then EvPopulationGeneration using this partition randomly shuffles blocks between individuals. 
 *  
 * @author Marcin Golebiowski, Krzysztof Sroka, Marek Chrusciel
 * 
 * PROFILED 09.IV.2007
 * 
 */
public class EvBinaryVectorECGAOperator implements EvOperator<EvBinaryVectorIndividual> {

  private int tournament_size;

  private boolean use_previous_structure;

  // solution space used for evaluation
  private EvBinaryVectorSpace solution_space;

  /*
   * we use EvStructureDiscover, EvPopulationGenerating and
   * EvTournamentSelection in this algorithm durning every iterators. Those
   * operators are stored here:
   */
  private ArrayList<EvOperator<EvBinaryVectorIndividual>> operators = new ArrayList<EvOperator<EvBinaryVectorIndividual>>();

  /**
   * 
   * 
   * @param tournament_size
   *      
   * @param use_previous_structure
   *          <ul>
   *          <li> if <code> true </code> then discovery of the best structure
   *          uses structure from previous iteration
   *          <li> if <code> false </code> then discovery of the best structure
   *          is done from initial structure.<BR>
   *          </ul>
   * @param solution_space
   *          used by operator
   */
  public EvBinaryVectorECGAOperator(int tournament_size,
      boolean use_previous_structure,
      EvBinaryVectorSpace solution_space) {
    
    /*
     * check if arguments are ok
     */
    if (tournament_size <= 0) {
      throw new IllegalArgumentException(
          "ECGA accepts only tournament_size as a parameter"
              + " which must be a positive Integer");
    }
    if (solution_space == null) {
      throw new IllegalArgumentException("solution_space cannot be null");
    }
    
    this.solution_space = solution_space;
    this.tournament_size = tournament_size;
    this.use_previous_structure = use_previous_structure;
    // initializing operators used by this operator
    init();
  }


  /**
   * Generated new better population from the given one.
   */
  public EvPopulation<EvBinaryVectorIndividual> apply(
      EvPopulation<EvBinaryVectorIndividual> population) {

    /*
     * error checking
     */
    if (population == null) {
      throw new IllegalArgumentException(" Applied population cannot be null ");
    }
    if (population.size() == 0) {
      throw new IllegalArgumentException(
          " Applied population must contain at leat one individual ");
    }

    /*
     * we apply 3 operators: EvStructureDiscover, EvPopulationGenerating and
     * EvTournamentSelection created durning initialization to given population
     */
    for (Iterator<EvOperator<EvBinaryVectorIndividual>> iterator = operators
        .iterator(); iterator.hasNext();) {
      EvOperator<EvBinaryVectorIndividual> operator = iterator.next();
      population = (EvPopulation<EvBinaryVectorIndividual>) operator
          .apply(population);
      /*
       * Some very basic sanity check. Population cannot be null or empty.
       */
      if (population == null) {
        throw new IllegalStateException("Operator " + operator.getClass()
            + " has returned null instead of valid population");
      }

      if (population.size() == 0) {
        throw new IllegalStateException("Operator " + operator.getClass()
            + " has shrunk population to zero.");
      }
    }

    return population;

  }

  //initializing operators used by this operator
  private void init() {

    EvStructureDiscover struct_update = new EvStructureDiscover(
        (EvBinaryVectorSpace) solution_space, use_previous_structure);
    EvPopulationGenerating pop_gen = new EvPopulationGenerating(struct_update,
        (EvBinaryVectorSpace) solution_space);

    operators.add((EvOperator<EvBinaryVectorIndividual>) 
        (new EvTournamentSelection<EvBinaryVectorIndividual>(tournament_size, 1)));
    operators.add(struct_update);
    operators.add(pop_gen);

  }  
  
}
